package com.game.main;

import com.game.global.Const;
import com.game.interfaces.Blast;
import com.game.interfaces.Draw;
import com.game.interfaces.LifeCycle;
import com.game.model.*;
import com.game.util.ImageUtil;

import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.List;
import java.util.*;

/**
 * Created by tjc on 2019-3-15.
 * 坦克操作控制器
 *
 * @author : 谭嘉诚
 * @Date: 2019-3-15
 */
public class GameContext extends JComponent implements KeyListener {

    private Stage curStage;
    private int enemyPtr;
    private int aliveEnemyNum;
    private int killedEnemies;
    private GameFlow flow;
    private boolean load = false;
    private Player player;
    private Home home;
    private StageSelector stageSelector;
    private ScoreBoard scoreBoard;

    public List<Tank> tankList = new LinkedList<>();
    public List<Wall> wallList = new LinkedList<>();
    public List<Bullet> bulletList = new LinkedList<>();
    public List<Blast> blastList = new LinkedList<>();
    public List<Border> borderList = new LinkedList<>();
    public List<BirthPlace> birthPlaceList = new LinkedList<>();

    private Map<Integer, PaintHandler> paintHandlerMap = new HashMap<>();

    {
        paintHandlerMap.put(GameFlow.SELECT_STAGE.value, new StageSelectorPaintHandler());
        paintHandlerMap.put(GameFlow.PLAYING_GAME.value, new PlayingGamePaintHandler());
        paintHandlerMap.put(GameFlow.GAME_OVER.value, new GameOverPaintHandler());
    }

    private static GameContext instance;

    private GameContext() {
    }

    public static synchronized GameContext getContext() {
        if (instance == null) {
            instance = new GameContext();
            instance.setSize(Const.DEFAULT_PLAY_WIDTH, Const.DEFAULT_PLAY_LENGTH);
            instance.setBackground(Color.BLACK);
        }
        return instance;
    }

    public void start() {
        flow = GameFlow.SELECT_STAGE;

        stageSelector = new StageSelector("stage.conf");
        addKeyListener(stageSelector);
        addKeyListener(this);
        setFocusable(true);
        new Thread(this::paintFlash).start();
    }

    public void startGame(Stage stage) {
        clearCache();
        loadStage(stage);
        for (BirthPlace b : birthPlaceList) {
            b.init();
        }
        for (Tank t : tankList) {
            t.init();
        }
        for (Wall w : wallList) {
            w.init();
        }
        home.init();
        scoreBoard.init();
        flow = GameFlow.PLAYING_GAME;
    }

    private void clearCache() {
        this.aliveEnemyNum = 0;
        this.killedEnemies = 0;
        tankList.forEach(Tank::destroy);
        birthPlaceList.forEach(BirthPlace::destroy);
        wallList.forEach(Wall::destroy);
        blastList.forEach(Blast::destroy);
        bulletList.forEach(Bullet::destroy);
        tankList.clear();
        birthPlaceList.clear();
        wallList.clear();
        blastList.clear();
        bulletList.clear();
        if (home != null) home.destroy();
        if (scoreBoard != null) scoreBoard.destroy();
        home = null;
        scoreBoard = null;
    }

    @Override
    public void paintComponent(Graphics g) {
        if (!load) {
            ImageUtil.loadAllImgs(g);
            load = true;
        }
        PaintHandler paintHandler = paintHandlerMap.get(flow.value);
        paintHandler.paint(g);
    }

    private void productEnemy() {
        while (aliveEnemyNum < curStage.getEnemyMaxNum() && enemyPtr < curStage.getEnemyNum()) {
            TankConfig config = new TankConfig();
            BirthPlace birthPlace = birthPlaceList.get(new Random().nextInt(3));
            config.setX(birthPlace.getLocalX());
            config.setY(birthPlace.getLocalY());
            config.setY(TankConfig.ENEMY);
            birthPlace.create(config);
            aliveEnemyNum++;
            enemyPtr++;
        }
    }

    void draw(List<? extends Draw> list, Graphics g) {
        for (Draw d : list) {
            draw(d, g);
        }
    }

    void draw(Draw d, Graphics g) {
        d.draw(g);
    }

    void clear(List<? extends LifeCycle> list) {
        Iterator<? extends LifeCycle> iterator = list.iterator();
        while (iterator.hasNext()) {
            LifeCycle o = iterator.next();
            if (!o.alive()) {
                beforeClearEvent(o);
                iterator.remove();
                afterClearEvent(o);
            }
        }
    }

    void beforeClearEvent(LifeCycle o) {
        if (o instanceof Enemy) {
            killedEnemies++;
        }
    }

    void afterClearEvent(LifeCycle o) {

    }

    /**
     * 刷新图像线程
     */
    private void paintFlash() {
        while (true) {
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            repaint();
        }
    }


    public boolean win() {
        return killedEnemies == curStage.getEnemyNum();
    }

    public boolean gameover() {
        if (!player.alive() || !home.alive()) {
            for (Tank t : tankList) {
                if (t.getTankType() == Tank.T_HERO) t.destroy();
            }
            return true;
        }
        return false;
    }

    public void loadStage(Stage stage) {
        // 默认选择第一关
        curStage = stage;
        enemyPtr = 0;
        ModuleFactory factory = ModuleFactory.getInstance(stage.getMapPath());
        borderList.addAll(factory.buildBorders());
        wallList.addAll(factory.buildWallList());
        birthPlaceList.addAll(factory.buildBirthPlaces());
        tankList.addAll(factory.buildHeros());
        home = factory.buildHome();
        this.player = new Player();
        this.scoreBoard = new ScoreBoard();
        //获取焦点
        setFocusable(true);
    }

    public List<Tank> getTankList() {
        return tankList;
    }

    public void addPoints(int point) {
        scoreBoard.addPoints(point);
    }

    public List<Wall> getWallList() {
        return wallList;
    }

    public List<Bullet> getBulletList() {
        return bulletList;
    }

    public void removeWall(Wall wall) {
        wallList.remove(wall);
    }

    public Home getHome() {
        return home;
    }

    public Player getPlayer() {
        return player;
    }

    public List<Border> getBorderList() {
        return this.borderList;
    }

    private interface PaintHandler {
        void paint(Graphics g);
    }

    private class StageSelectorPaintHandler implements PaintHandler {

        @Override
        public void paint(Graphics g) {
            g.setColor(Color.GRAY);
            g.fillRect(0, 0, Const.DEFAULT_PLAY_WIDTH, Const.DEFAULT_PLAY_LENGTH);
            stageSelector.draw(g);
        }
    }

    private class PlayingGamePaintHandler implements PaintHandler {

        @Override
        public void paint(Graphics g) {
            productEnemy();
            // todo 设置为组件背景颜色
            g.setColor(Color.BLACK);
            g.fillRect(0, 0, Const.DEFAULT_PLAY_WIDTH, Const.DEFAULT_PLAY_LENGTH);
            draw(tankList, g);
            clear(tankList);
            draw(wallList, g);
            draw(scoreBoard, g);
            clear(wallList);
            synchronized (bulletList) {
                draw(bulletList, g);
            }
            clear(bulletList);
            synchronized (blastList) {
                draw(blastList, g);
            }
            clear(blastList);
            draw(birthPlaceList, g);
            draw(home, g);

            if (win()) {
                System.out.println("you win.");
            }
            if (gameover()) {
                flow = GameFlow.GAME_OVER;
            }
        }
    }

    private class GameOverPaintHandler implements PaintHandler {

        @Override
        public void paint(Graphics g) {
            paintHandlerMap.get(GameFlow.PLAYING_GAME.value).paint(g);
            g.drawImage(ImageUtil.gameOverImage, 0, 0, 100, 100, null);
        }
    }

    @Override
    public void keyTyped(KeyEvent e) {

    }

    @Override
    public void keyPressed(KeyEvent e) {
        if (e.getKeyCode() == KeyEvent.VK_1) {
            System.out.println("restart game...");
            startGame(curStage);
        }
    }

    @Override
    public void keyReleased(KeyEvent e) {
    }
}
